package io.github.cchenxi.crpc.core.provider;

import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import io.github.cchenxi.crpc.core.annotation.RpcProvider;
import io.github.cchenxi.crpc.core.api.RegistryCenter;
import io.github.cchenxi.crpc.core.meta.InstanceMeta;
import io.github.cchenxi.crpc.core.meta.ProviderMeta;
import io.github.cchenxi.crpc.core.meta.ServiceMeta;
import io.github.cchenxi.crpc.core.utils.MethodUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.Data;
import lombok.SneakyThrows;

/**
 * 服务提供者启动类
 * Date: 2024-03-07
 *
 * @author chenxi
 */
@Data
public class ProviderBootstrap implements ApplicationContextAware {
    ApplicationContext applicationContext;

    RegistryCenter registryCenter;

    private MultiValueMap<String, ProviderMeta> skeleton = new LinkedMultiValueMap<>();

    private InstanceMeta instance;

    @Value("${server.port}")
    private String port;

    @Value("${app.id}")
    private String app;

    @Value("${app.namespace}")
    private String namespace;

    @Value("${app.env}")
    private String env;

    @SneakyThrows
    @PostConstruct
    public void init() {
        registryCenter = applicationContext.getBean(RegistryCenter.class);
        Map<String, Object> providers = applicationContext.getBeansWithAnnotation(RpcProvider.class);
        providers.values().forEach(this::genInterface);
    }

    @SneakyThrows
    public void start() {
        String host = InetAddress.getLocalHost().getHostAddress();
        instance = InstanceMeta.http(host, Integer.valueOf(port));
        registryCenter.start();
        skeleton.keySet().forEach(this::registerService);
    }

    @PreDestroy
    public void stop() {
        System.out.println("===> unregister all service.");
        skeleton.keySet().forEach(this::unregisterService);
        registryCenter.stop();
    }

    private void registerService(String service) {
        ServiceMeta serviceMeta = ServiceMeta.builder().app(app).namespace(namespace).env(env).name(service).build();
        registryCenter.register(serviceMeta, instance);
    }

    private void unregisterService(String service) {
        ServiceMeta serviceMeta = ServiceMeta.builder().app(app).namespace(namespace).env(env).name(service).build();
        registryCenter.unregister(serviceMeta, instance);
    }

    private void genInterface(Object impl) {
        Class<?> interfaceClazz = impl.getClass().getInterfaces()[0];
        Method[] methods = interfaceClazz.getMethods();
        for (Method method : methods) {
            if (MethodUtil.checkLocalMethod(method)) {
                continue;
            }
            createProvider(interfaceClazz, impl, method);
        }
    }

    private void createProvider(Class<?> service, Object impl, Method method) {
        ProviderMeta providerMeta = ProviderMeta.builder()
                .method(method)
                .serviceImpl(impl)
                .methodSign(MethodUtil.methodSign(method)).build();
        System.out.println("create a provider: " + providerMeta);
        skeleton.add(service.getCanonicalName(), providerMeta);
    }
}
